﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
using Qreed.Diagnostics;

namespace GotLyrics.Dialogs
{
    public partial class GetTagsFromFilenamesDialog : BaseDialog
    {
        private const string IgnoreTagName = "Ignore Selection";

        /// <summary>
        /// Initializes a new instance of the <see cref="GetTagsFromFilenamesDialog"/> class.
        /// </summary>
        /// <param name="audioFiles">The audio files.</param>
        public GetTagsFromFilenamesDialog(AudioFileContext[] audioFiles, GotLyricsMainForm mainForm) : base(mainForm)
        {
            InitializeComponent();

            AudioFiles = audioFiles;

            InitializeToolTip();

            // Set the scope text to the first audio file

            scopeTextBox.Text = Path.GetFileNameWithoutExtension(audioFiles[0].Filename);

            // Initialize the dictionary with the selectable fields

            _fields.Add("Album Title", false);
            _fields.Add("Artist Name", false);
            _fields.Add("Comment", false);
            _fields.Add("Genre", false);
            _fields.Add(IgnoreTagName, false);
            _fields.Add("Song Title", false);
            _fields.Add("Track Number", false);
            _fields.Add("Year", false);
            
            // Initialize the preview list

            InitializePreview();
        }

        private void InitializePreview()
        {
            foreach (AudioFileContext audioFile in AudioFiles)
            {
                ListViewItem item = new ListViewItem();
                item.Checked = true;
                item.Tag = audioFile;

                string filename = Path.GetFileName(audioFile.Filename);
                item.SubItems.Add(filename);
                
                listPreview.Items.Add(item);
            }

            ResizeColumns();
        }

        private void InitializeToolTip()
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Select a piece of text and rightclick to create a new scope.");

            toolTipScopeTextBox.ToolTipText = sb.ToString();
            toolTipScopeTextBox.TargetControl = scopeTextBox;
        }

        /// <summary>
        /// A dictionary that maintains the list of already used fields
        /// </summary>
        private Dictionary<string, bool> _fields = new Dictionary<string, bool>();

        private AudioFileContext[] _audioFiles;
        /// <summary>
        /// Gets or sets the audio files.
        /// </summary>
        /// <value>The audio files.</value>
        public AudioFileContext[] AudioFiles
        {
            get { return this._audioFiles; }
            set { this._audioFiles = value; }
        }
	
        private void scopeTextBox_CreateScope(object sender, Qreed.Windows.ScopeTextBoxEventArgs e)
        {
            SelectScopeNameDialog dlg = new SelectScopeNameDialog(_fields, MainForm);

            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                if (dlg.SelectedField != IgnoreTagName) // Ignore field should be reselectable
                    _fields[dlg.SelectedField] = true;

                e.Name = dlg.SelectedField;
            }
            else
                e.Cancel = true;
        }

        private void scopeTextBox_RegularExpressionChanged(object sender, EventArgs e)
        {
            UpdatePreview();
        }

        private void UpdatePreview()
        {
            SuspendLayout();

            while (listPreview.Columns.Count > 2)
                listPreview.Columns.RemoveAt(listPreview.Columns.Count - 1);

            // Update the regular expression so we match everything after the first group

            string regularExpression = UpdateRegularExpression(scopeTextBox.RegularExpression);

            Regex regex = new Regex(regularExpression);

            MainForm.Log.Write("Updating preview.", "Regular expression: \"" + regularExpression + "\"", LogLevel.Debug);

            foreach (ListViewItem lvItem in listPreview.Items)
            {
                while (lvItem.SubItems.Count > 2)
                    lvItem.SubItems.RemoveAt(lvItem.SubItems.Count - 1);

                AudioFileContext audioFile = (AudioFileContext)lvItem.Tag;
                string filename = Path.GetFileNameWithoutExtension(audioFile.Filename);

                Match match = regex.Match(filename);

                if (match.Success)
                {
                    for (int i = 1; i < match.Groups.Count; i++)
                    {
                        string tagName = regex.GroupNameFromNumber(i);
                        
                        if (string.IsNullOrEmpty(tagName))
                            continue;

                        tagName = tagName.Replace('_', ' ');

                        if (tagName == IgnoreTagName)
                            continue;

                        Group group = match.Groups[i];
                        string value = group.Value.Trim();

                        if (ReplaceUnderscores)
                            value = value.Replace('_', ' ');

                        value = WordCasingOptionsControl.Case(value, WordCasing);

                        if (!listPreview.Columns.ContainsKey(tagName))
                            listPreview.Columns.Add(tagName, tagName);
                        
                        int columnIndex = listPreview.Columns.IndexOfKey(tagName);

                        if (!lvItem.SubItems.ContainsKey(tagName))
                        {
                            ListViewItem.ListViewSubItem subItem = new ListViewItem.ListViewSubItem();
                            subItem.Text = value;
                            subItem.Name = tagName;

                            lvItem.SubItems.Add(subItem);
                        }
                        else
                        {
                            lvItem.SubItems[tagName].Text = value;
                        }
                    }
                }
            }

            ResizeColumns();
            
            ResumeLayout(false);
        }

        private void ResizeColumns()
        {
            for (int i = 1; i < listPreview.Columns.Count; i++)
            {
                ColumnHeader columnHeader = listPreview.Columns[i];
                columnHeader.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);

                Size s = TextRenderer.MeasureText(columnHeader.Text, listPreview.Font);

                columnHeader.Width = Math.Max(s.Width + 20, columnHeader.Width);
            }
        }

        private string UpdateRegularExpression(string input)
        {
            try
            {
                string pattern = Regex.Escape("(?<") + ".+?" + Regex.Escape(">.+?)");

                MatchCollection matches = Regex.Matches(input, pattern);

                if (matches.Count > 0)
                {
                    Match m = matches[matches.Count - 1];

                    int endPos = m.Index + m.Length;

                    if (endPos < input.Length)
                    {
                        string end = input.Substring(endPos);
                        end = Regex.Unescape(end);

                        for (int i = 0; i < end.Length; i++)
                        {
                            char c = end[i];

                            if (Char.IsLetterOrDigit(c))
                            {
                                end = Regex.Escape(end.Substring(0, i));
                                end += "+.?";
                                input = input.Substring(0, endPos) + end;
                                break;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MainForm.Log.Write("Error occured while updating regular expression: \"" + input + "\".", ex);
            }

            return input;
        }

        private void scopeTextBox_RemoveScope(object sender, Qreed.Windows.ScopeTextBoxEventArgs e)
        {
            _fields[e.Name] = false; // Mark the field available again
        }

        private void btOk_Click(object sender, EventArgs e)
        {
            IList<ListViewItem> lvItems = columnHeaderCheckBox.GetCheckedItems();

            foreach (ListViewItem listViewItem in lvItems)
            {
                AudioFileContext filecontext = (AudioFileContext)listViewItem.Tag;

                for (int i = 1; i < listPreview.Columns.Count; i++)
                {
                    if (i >= listViewItem.SubItems.Count)
                        continue;

                    ListViewItem.ListViewSubItem subItem = listViewItem.SubItems[i];

                    string tagName = listPreview.Columns[i].Text;

                    switch (tagName)
                    {
                        case IgnoreTagName:
                            continue;

                        case "Album Title":
                            filecontext.Tags.Album = subItem.Text;
                            filecontext.IsModified = true;
                            break;

                        case "Artist Name":
                            filecontext.Tags.Artist = subItem.Text;
                            filecontext.IsModified = true;
                            break;

                        case "Comment":
                            filecontext.Tags.Comments = subItem.Text;
                            filecontext.IsModified = true;
                            break;

                        case "Genre":
                            // TODO!
                            break;

                        case "Song Title":
                            filecontext.Tags.Title = subItem.Text;
                            filecontext.IsModified = true;
                            break;

                        case "Track Number":
                            filecontext.Tags.TrackNum = short.Parse(subItem.Text);
                            filecontext.IsModified = true;
                            break;
            
                        case "Year":
                            filecontext.Tags.Year = short.Parse(subItem.Text);
                            filecontext.IsModified = true;
                            break;

                        default:
                            continue;
                    }
                }

                if (filecontext.IsModified)
                    filecontext.ReflectListViewItem();
            }

            DialogResult = DialogResult.OK;
            Close();
        }

        private bool ReplaceUnderscores
        {
            get { return Settings.Default.GetTagsFromFilesReplaceUnderscores; }
            set { Settings.Default.GetTagsFromFilesReplaceUnderscores = value; }
        }

        private WordCasingType WordCasing
        {
            get { return (WordCasingType)Enum.Parse(typeof(WordCasingType), Settings.Default.GetTagsFromFilesWordCasing); }
            set { Settings.Default.GetTagsFromFilesWordCasing = value.ToString(); }
        }

        private void btOptions_Click(object sender, EventArgs e)
        {
            FilenameOptionsDialog dlg = new FilenameOptionsDialog(MainForm);

            dlg.ReplaceSpaces = ReplaceUnderscores;
            dlg.WordCasing = WordCasing;

            dlg.CheckBoxText = "Replace underscores with spaces.";

            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                ReplaceUnderscores = dlg.ReplaceSpaces;
                WordCasing = dlg.WordCasing;

                UpdatePreview();
            }
        }	
    }
}
